package com.runa.monitor.platinterface.netservice.equipment;

import com.runa.monitor.comm.dto.Archive;
import com.runa.monitor.comm.dto.Node;
import com.runa.monitor.comm.dto.Point;
import com.runa.monitor.comm.dto.PointValue;
import com.runa.monitor.comm.dto.newComm.HotMeterVO;
import com.runa.monitor.comm.dto.newComm.ValveVO;
import com.runa.monitor.platinterface.backwork.impl.SelfTaskFunction;
import com.runa.protocol.util.UtilTime;
import com.runa.monitor.platinterface.base.BaseContainer;
import com.runa.monitor.platinterface.backwork.function.TaskFunction;
import com.runa.monitor.platinterface.netservice.connect.commstatus.CommStatus;
import com.runa.monitor.platinterface.netservice.server.ListenerServer;
import com.runa.protocol.enums.DtuModel;
import com.runa.protocol.enums.ModbusType;
import com.runa.rccprotocol.dto.AffirmDeny;
import com.runa.rccprotocol.dto.equdata.ElectricityMeterData;
import com.runa.rccprotocol.dto.equdata.EquData;
import com.runa.rccprotocol.dto.param.ExtendParam;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
public class PublicBuildDriver extends NetEqu<String> {

    private ListenerServer listener;

    private static List<Point> points;

    private static List<Integer> ids;

    static {
        ids = Arrays.asList(40050, 40052, 40054, 40056, 40058, 40060, 40062, 40063, 40067,
                40068, 40070, 40072, 40074, 40076, 40077, 40078, 40079, 40080, 40081, 40082, 40083, 40084, 40085, 40087, 40089, 40091, 40093, 40094,40095, 40097, 40099, 40101, 40103, 40104, 40106, 40108, 40110, 40112,
                40129, 40131, 40133, 40135, 40137, 40139, 40141, 40142, 40146,
                40147, 40149, 40151, 40153, 40155, 40156, 40157, 40158, 40159, 40160, 40161, 40162, 40163, 40164, 40166, 40168, 40170, 40172, 40173, 40174, 40176, 40178, 40180, 40182, 40183,40185, 40187, 40189, 40191,
                40016, 40018, 40020
        );
        points = new ArrayList<>();
        //1#热表
        //累热
        points.add(new Point(ids.get(0), "浮点", "CDAB", 0));
        //累流
        points.add(new Point(ids.get(1), "浮点", "CDAB", 0));
        //瞬热
        points.add(new Point(ids.get(2), "浮点", "CDAB", 0));
        //瞬流
        points.add(new Point(ids.get(3), "浮点", "CDAB", 0));
        //供温
        points.add(new Point(ids.get(4), "浮点", "CDAB", 0));
        //回温
        points.add(new Point(ids.get(5), "浮点", "CDAB", 0));
        //通讯状态
        points.add(new Point(ids.get(6), "16位整型", "AB", 0));
        //通讯时间
        points.add(new Point(ids.get(7), "双精度浮点", "ABCDEFGH", 0));
        //采集状态
        points.add(new Point(ids.get(8), "16位整型", "AB", 0));
        //1#阀门
        //供压
        points.add(new Point(ids.get(9), "浮点", "CDAB", 0));
        //回压I
        points.add(new Point(ids.get(10), "浮点", "CDAB", 0));
        //供温
        points.add(new Point(ids.get(11), "浮点", "CDAB", 0));
        //回温
        points.add(new Point(ids.get(12), "浮点", "CDAB", 0));
        //给定开度
        points.add(new Point(ids.get(13), "16位整型", "AB", 0));
        //反馈开度
        points.add(new Point(ids.get(14), "16位整型", "AB", 0));
        //反馈开度有效
        points.add(new Point(ids.get(15), "16位整型", "AB", 0));
        //方向
        points.add(new Point(ids.get(16), "16位整型", "AB", 0));
        //远程/本地
        points.add(new Point(ids.get(17), "16位整型", "AB", 0));
        //远程设定开度
        points.add(new Point(ids.get(18), "16位整型", "AB", 0));
        //手自动
        points.add(new Point(ids.get(19), "16位整型", "AB", 0));
        //手动开度设定
        points.add(new Point(ids.get(20), "16位整型", "AB", 0));
        //自动控制策略
        points.add(new Point(ids.get(21), "16位整型", "AB", 0));
        //目标温度设定
        points.add(new Point(ids.get(22), "浮点", "CDAB", 0));
        //目标流量设定
        points.add(new Point(ids.get(23), "浮点", "CDAB", 0));
        //当前温度
        points.add(new Point(ids.get(24), "浮点", "CDAB", 0));
        //当前流量
        points.add(new Point(ids.get(25), "浮点", "CDAB", 0));
        //开度上限
        points.add(new Point(ids.get(26), "16位整型", "AB", 0));
        //开度下限
        points.add(new Point(ids.get(27), "16位整型", "AB", 0));
        //追温P
        points.add(new Point(ids.get(28), "浮点", "CDAB", 0));
        //追温I
        points.add(new Point(ids.get(29), "浮点", "CDAB", 0));
        //追温D
        points.add(new Point(ids.get(30), "浮点", "CDAB", 0));
        //追温死区
        points.add(new Point(ids.get(31), "浮点", "CDAB", 0));
        //追温周期
        points.add(new Point(ids.get(32), "16位整型", "AB", 0));
        //追流量P
        points.add(new Point(ids.get(33), "浮点", "CDAB", 0));
        //追流量I
        points.add(new Point(ids.get(34), "浮点", "CDAB", 0));
        //追流量D
        points.add(new Point(ids.get(35), "浮点", "CDAB", 0));
        //追流量死区
        points.add(new Point(ids.get(36), "浮点", "CDAB", 0));
        //追流量周期
        points.add(new Point(ids.get(37), "16位整型", "AB", 0));
        //2#热表
        //累热
        points.add(new Point(ids.get(38), "浮点", "CDAB", 0));
        //累流
        points.add(new Point(ids.get(39), "浮点", "CDAB", 0));
        //瞬热
        points.add(new Point(ids.get(40), "浮点", "CDAB", 0));
        //瞬流
        points.add(new Point(ids.get(41), "浮点", "CDAB", 0));
        //供温
        points.add(new Point(ids.get(42), "浮点", "CDAB", 0));
        //回温
        points.add(new Point(ids.get(43), "浮点", "CDAB", 0));
        //通讯状态
        points.add(new Point(ids.get(44), "16位整型", "AB", 0));
        //通讯时间
        points.add(new Point(ids.get(45), "双精度浮点", "ABCDEFGH", 0));
        //采集状态
        points.add(new Point(ids.get(46), "16位整型", "AB", 0));
        //2#阀门
        //供压
        points.add(new Point(ids.get(47), "浮点", "CDAB", 0));
        //回压I
        points.add(new Point(ids.get(48), "浮点", "CDAB", 0));
        //供温
        points.add(new Point(ids.get(49), "浮点", "CDAB", 0));
        //回温
        points.add(new Point(ids.get(50), "浮点", "CDAB", 0));
        //给定开度
        points.add(new Point(ids.get(51), "16位整型", "AB", 0));
        //反馈开度
        points.add(new Point(ids.get(52), "16位整型", "AB", 0));
        //反馈开度有效
        points.add(new Point(ids.get(53), "16位整型", "AB", 0));
        //方向
        points.add(new Point(ids.get(54), "16位整型", "AB", 0));
        //远程/本地
        points.add(new Point(ids.get(55), "16位整型", "AB", 0));
        //远程设定开度
        points.add(new Point(ids.get(56), "16位整型", "AB", 0));
        //手自动
        points.add(new Point(ids.get(57), "16位整型", "AB", 0));
        //手动开度设定
        points.add(new Point(ids.get(58), "16位整型", "AB", 0));
        //自动控制策略
        points.add(new Point(ids.get(59), "16位整型", "AB", 0));
        //目标温度设定
        points.add(new Point(ids.get(60), "浮点", "CDAB", 0));
        //目标流量设定
        points.add(new Point(ids.get(61), "浮点", "CDAB", 0));
        //当前温度
        points.add(new Point(ids.get(62), "浮点", "CDAB", 0));
        //当前流量
        points.add(new Point(ids.get(63), "浮点", "CDAB", 0));
        //开度上限
        points.add(new Point(ids.get(64), "16位整型", "AB", 0));
        //开度下限
        points.add(new Point(ids.get(65), "16位整型", "AB", 0));
        //追温P
        points.add(new Point(ids.get(66), "浮点", "CDAB", 0));
        //追温I
        points.add(new Point(ids.get(67), "浮点", "CDAB", 0));
        //追温D
        points.add(new Point(ids.get(68), "浮点", "CDAB", 0));
        //追温死区
        points.add(new Point(ids.get(69), "浮点", "CDAB", 0));
        //追温周期
        points.add(new Point(ids.get(70), "16位整型", "AB", 0));
        //追流量P
        points.add(new Point(ids.get(71), "浮点", "CDAB", 0));
        //追流量I
        points.add(new Point(ids.get(72), "浮点", "CDAB", 0));
        //追流量D
        points.add(new Point(ids.get(73), "浮点", "CDAB", 0));
        //追流量死区
        points.add(new Point(ids.get(74), "浮点", "CDAB", 0));
        //追流量周期
        points.add(new Point(ids.get(75), "16位整型", "AB", 0));
        //电表
        //电压
        points.add(new Point(ids.get(76), "浮点", "CDAB", 0));
        //电流
        points.add(new Point(ids.get(77), "浮点", "CDAB", 0));
        //电量
        points.add(new Point(ids.get(78), "浮点", "CDAB", 0));
    }

    

    public PublicBuildDriver(String serial, Integer timeout, ListenerServer listener) {
        super(serial, timeout);
        this.listener = listener;
        PlcService service = new PublicDtuPlcService(1,1,
                serial(), null, timeout(), ModbusType.MODBUS_RTU, DtuModel.HongDian7710,  null, listener);
        try {
            service.init(points);
        } catch (Exception e) {
            log.error("plc service初始化失败", e);
            return;
        }
        setPlcService(service);
    }


    @Override
    public void readNodeEqu(List<Node> nodeList, TaskFunction function, EquData.EquDataType type) {
        switch (type) {
            case METER:
                readBuildMeter(nodeList, function);
                break;
            case VALVE:
                break;
            case PUBLIC_UNIT:
                readBuildValve(nodeList, function);
                break;
            case TEMPERATURE_PANEL:
                break;
            default:
        }
    }

    public void readElectricityMeter() {
        PlcService plc = getPlcService(1);
        if (plc.session().isOnline()) {
            Map<String, Object> res = plc.query(ids.subList(76, 79));
            if (CommStatus.OK.getStatus().equals(res.get("status"))) {
                Map<Integer, String> data = (Map<Integer, String>) res.get("data");
                ElectricityMeterData meterData = new ElectricityMeterData();
                meterData.setTotal(Float.parseFloat(data.get(ids.get(78))));
                new SelfTaskFunction(serial()).doApply(CommStatus.OK, Collections.singletonList(meterData));
            }
        }
    }

    private void readBuildMeter(List<Node> nodes, TaskFunction function) {
        CommStatus status = CommStatus.OK;
        for (Node node : nodes) {
            if (node != null) {
                PlcService plc = getPlcService(1);
                function.setStatus(Collections.singletonList(node));
                if (plc.session().isOnline()) {
                    List<Integer> meterIds = null;
                    if (node.getSerial().startsWith("1")) {
                        meterIds = ids.subList(0, 9);
                    } else if (node.getSerial().startsWith("2")) {
                        meterIds = ids.subList(38, 47);
                    }
                    Map<String, Object> res = plc.query(meterIds);
                    HotMeterVO meterData = new HotMeterVO();
                    meterData.setIndexNo(node.getIndex());
                    if (CommStatus.OK.getStatus().equals(res.get("status"))) {
                        Map<Integer, String> data = (Map<Integer, String>) res.get("data");
                        meterData.setAccHeat(Float.parseFloat(data.get(meterIds.get(0)))  * 278);
                        meterData.setAccFlow(Float.parseFloat(data.get(meterIds.get(1))));
                        meterData.setPower(Float.parseFloat(data.get(meterIds.get(2))) * 278);
                        meterData.setFlow(Float.parseFloat(data.get(meterIds.get(3))));
                        meterData.setSupplyTemp(Float.parseFloat(data.get(meterIds.get(4))));
                        meterData.setReturnTemp(Float.parseFloat(data.get(meterIds.get(5))));
                        String readDate = new BigDecimal(data.get(meterIds.get(7))).toString();
                        meterData.setReadDate(UtilTime.StringToDate(readDate, "yyyyMMddHHmmss"));
                    } else {
                        meterData.setSuccess(false);
                        meterData.setStatus("FFFF");
                    }
                    function.doApply(status, Collections.singletonList(meterData));
                } else {
                    function.doApply(CommStatus.OffLine, Collections.singletonList(node));
                    break;
                }
            }
        }
    }

    private DtuPlcService getPlcService(Integer id) {
        for (DtuPlcService plcService : BaseContainer.getDtu(serial())) {
            if (plcService.getId().equals(id)) return plcService;
        }
        return null;
    }

    private void setPlcService(PlcService plcService) {
        BaseContainer.putPlcService(plcService);
    }

    private void readBuildValve(List<Node> buildValves, TaskFunction function) {
        for (Node node : buildValves) {
            if (node != null) {
                PlcService plc = getPlcService(1);
                function.setStatus(Collections.singletonList(node));
                if (plc != null && plc.session().isOnline()) {
                    log.info("{}  {}  读取成功", serial(), node.getSerial());
                    ValveVO valveVO = new ValveVO();
                    List<Integer> valveIds = null;
                    if (node.getSerial().startsWith("1")) {
                        valveIds = ids.subList(9, 38);
                    } else if (node.getSerial().startsWith("2")) {
                        valveIds = ids.subList(47, 76);
                    }
                    Map<String, Object> res = plc.query(valveIds);
                    valveVO.setValveSerial(node.getSerial());
                    if (CommStatus.OK.getStatus().equals(res.get("status"))) {
                        Map<Integer, String> data = (Map<Integer, String>) res.get("data");
                        valveVO.setSupplyPre(Float.parseFloat(data.get(valveIds.get(0))));
                        valveVO.setReturnPre(Float.parseFloat(data.get(valveIds.get(1))));
                        valveVO.setSupplyTemp(Float.parseFloat(data.get(valveIds.get(2))));
                        valveVO.setReturnTemp(Float.parseFloat(data.get(valveIds.get(3))));
                        valveVO.setOuputOpen(Float.parseFloat(data.get(valveIds.get(4))));
                        valveVO.setActOpen(Float.parseFloat(data.get(valveIds.get(5))));
                        valveVO.setLR(Integer.parseInt(data.get(valveIds.get(8))) == 0 ? ValveVO.LREnum.本地 : ValveVO.LREnum.远程);
                        valveVO.setRemoteSet(Float.parseFloat(data.get(valveIds.get(9))));
//                        valveVO.setEnvTemp(Float.parseFloat(data.get(valveIds.get(15))));
                        valveVO.setFlow(Float.parseFloat(data.get(valveIds.get(16))));
                        valveVO.setOpenUL(Float.parseFloat(data.get(valveIds.get(17))));
                        valveVO.setOpenDL(Float.parseFloat(data.get(valveIds.get(18))));
                        valveVO.setModel(ValveVO.ModelEnum.开度);
                    } else {
                        log.info("{}  {}  读取失败: {}", serial(), node.getSerial(), res.get("status"));
                        valveVO.setSuccess(false);
                        valveVO.setStatus("FFFF");
                    }
                    function.doApply(CommStatus.OK, Collections.singletonList(valveVO));
                }
            }
        }
    }

    @Override
    public void setNodeEquArchive(List<Archive> archives, TaskFunction function) {

    }

    @Override
    public void clearNodeEquArchive(TaskFunction function) {

    }

    @Override
    public void valveOnOffControl(List<Node> valveSerial, boolean ifLock, Integer opening, TaskFunction function) {

    }

    @Override
    public void readNodeRecord(List<Integer> indexes, TaskFunction function) {

    }

    @Override
    public void sentPublicUnitRecord(List<Node> valves, Integer model, Map param, TaskFunction function) {
        for (Node valve : valves) {
            if (valve != null) {
                PlcService plc = getPlcService(1);
                if (!plc.session().isOnline()) {
                    function.doApply(CommStatus.OffLine, Collections.singletonList(valve));
                    break;
                }
                List<PointValue> pointValues = null;
                switch (model) {
                    case 1:
                        pointValues = new ArrayList<>();
                        pointValues.add(new PointValue(valve.getSerial().startsWith("1") ? ids.get(18) : ids.get(56),
                                param.get("targetValue").toString()));
                        break;
                    case 2:
                    case 3:
                    case 4:
                        break;
                    default:
                }
                if (pointValues != null) {
                    Map<String, Object> res = plc.constructAndSendWriteRequest(pointValues);
                    function.setStatus(valve);
                    if (res.get("status").equals(CommStatus.OK.getStatus())) {
                        function.doApply(CommStatus.OK, new AffirmDeny() {
                            @Override
                            public Boolean getData() {
                                return true;
                            }
                        });
                    } else {
                        function.doApply(CommStatus.OK, new AffirmDeny() {
                            @Override
                            public Boolean getData() {
                                return false;
                            }
                        });
                    }
                }
            }
        }
    }

    @Override
    public void sentPublicUnitModel(List<Node> valves, boolean model, TaskFunction function) {
        for (Node valve : valves) {
            if (valve != null) {
                PlcService plc = getPlcService(1);
                if (!plc.session().isOnline()) {
                    function.doApply(CommStatus.OffLine, Collections.singletonList(valve));
                    break;
                }
                List<PointValue> pointValues;
                pointValues = new ArrayList<>();
                pointValues.add(new PointValue(valve.getSerial().startsWith("1") ? ids.get(17) : ids.get(55), String.valueOf(model ? 1 : 0)));
                Map<String, Object> res = plc.constructAndSendWriteRequest(pointValues);
                function.setStatus(valve);
                if (res.get("status").equals(CommStatus.OK.getStatus())) {
                    function.doApply(CommStatus.OK, new AffirmDeny() {
                        @Override
                        public Boolean getData() {
                            return true;
                        }
                    });
                } else {
                    function.doApply(CommStatus.OK, new AffirmDeny() {
                        @Override
                        public Boolean getData() {
                            return false;
                        }
                    });
                }
            }
        }
    }

    @Override
    public void sentExtendParam(ExtendParam param, TaskFunction function) {

    }

    @Override
    public void readPublicUnitRecord(Node valveSerial, Integer model, TaskFunction function) {

    }

    @Override
    public void readPublicUnitAndMeter(Node valveSerial, Node meterSerial, TaskFunction function) {

    }

    @Override
    public void heartbeatUpdate() {

    }

    @Override
    public boolean heartStop() {
        return false;
    }

}
